/* hipopt.c
 * This file is part of hipopt 
 * hipopt is a simple command line arguments parser C library.
 * 
 * Copyright (C) 2008 - Pablo Rauzy
 * 
 * hipopt is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * hipopt is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */


#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "hipopt.h"

int
hopt_countargs (char *args[])
{
  int argc = 0;
  
  for (;;) {
    if (!args[argc]) break;
    if (!strcmp(args[argc], "--")) break;
    argc++;
  }
  
  return argc;
}

Hipopt *
hopt_newopt(const char arg, int idx)
{
  Hipopt *opt;
  
  opt = malloc(sizeof(Hipopt));
  assert(opt != NULL);
  
  opt->name = malloc(sizeof(char));
  assert(opt->name != NULL);
  *(opt->name) = arg;
  
  opt->value = NULL;
  
  opt->index = idx;
  
  return opt;
}

Hipopt *
hopt_newlongopt (const char *arg, int idx)
{
  Hipopt *opt;
  char *name, *value;
  
  char *assign = strchr(arg, '=');
  if (!assign) {
    name = malloc((strlen(arg) - 1) * sizeof(char));
    assert(name != NULL);
    strcpy(name, arg + 2);
    value = NULL;
  }
  else {
    int namelen = assign - (arg + 1);
    int valuelen = strlen(arg) - (assign - arg);
    
    name = malloc(namelen * sizeof(char));
    assert(name != NULL);
    strncpy(name, arg + 2, namelen - 1);
    name[namelen - 1] = 0;
    
    value = malloc(valuelen * sizeof(char));
    assert(value != NULL);
    strcpy(value, ++assign);
  }
  
  opt = malloc(sizeof(Hipopt));
  assert(opt != NULL);
  opt->name = name;
  opt->value = value;
  opt->index = idx;
  
  return opt;
}

Hipopt *
hopt_setvalue (Hipopt *opt, char *arg)
{
  if (opt->value != NULL) free(opt->value);
  opt->value = malloc((strlen(arg) + 1) * sizeof(char));
  assert(opt->value != NULL);
  strcpy(opt->value, arg);
  
  return opt;
}

Hipopts *
hopt_init (char *argv[])
{
  char **args = argv + 1;
  int argc, i, n;
  Hipopts *options;
  
  options = malloc(sizeof(Hipopts));
  assert(options != NULL);
  
  argc = hopt_countargs(args);
  options->opts = malloc(argc * sizeof(Hipopt *));
  assert(options->opts != NULL);
  
  options->count = argc;
  if (!args[argc]) options->lastopt = 0;
  else options->lastopt = argc + 1;
  /*
    i is used to iterate over args[], n over options->opts[].
    this is because options->count can change, but argc can't.
  */
  for (i = 0, n = 0; n < options->count; i++, n++) {
    if (args[i][0] == '-' && args[i][1] == '-') {
      options->opts[n] = hopt_newlongopt(args[i], i+1);
      if (options->opts[n]->value == NULL
          && (i + 1) < argc && args[i+1][0] != '-') {
        i++;
        options->count--;
        options->opts = realloc(options->opts,
                                options->count * sizeof(Hipopt *));
        hopt_setvalue(options->opts[n], args[i]);
      }
    }
    else if (args[i][0] == '-') {
      int nb_opts = strlen(args[i]) - 1;
      if (nb_opts == 1) {
        options->opts[n] = hopt_newopt(args[i][1], i+1);
        if (options->opts[n]->value == NULL
            && (i + 1) < argc && args[i+1][0] != '-') {
          i++;
          options->count--;
          options->opts = realloc(options->opts,
                                  options->count * sizeof(Hipopt *));
          hopt_setvalue(options->opts[n], args[i]);
        }
      }
      else {
        int j;
        options->count += nb_opts - 1;
        options->opts = realloc(options->opts,
                                options->count * sizeof(Hipopt *));
        for (j = 0; j < nb_opts; j++) {
          options->opts[n+j] = hopt_newopt(args[i][j+1], i+1);
        }
        n += nb_opts - 1;
      }
    }
    else {
      options->count--;
      options->opts = realloc(options->opts, options->count * sizeof(Hipopt *));
      n--;
    }
  }
  
  return options;
}

int
hopt_count (Hipopts *options)
{
  if (options == NULL) return 0;
  
  return options->count;
}

char *
hopt_get (const char *opt_name, Hipopts *options)
{
  int i;
  
  for (i = options->count - 1; i > -1; i--) {
    if (!strcmp(options->opts[i]->name, opt_name)) {
      if (options->opts[i]->value != NULL) return options->opts[i]->value;
      else return "set";
    }
  }
  
  return NULL;
}

char *
hopt_getn (int opt_num, Hipopts *options)
{
  if (options == NULL) return NULL;
  
  if (opt_num < 0 && options->count + opt_num >= 0) opt_num += options->count;
    
  if (options->opts[opt_num]->value != NULL)
    return options->opts[opt_num]->value;
  else return "set";
  
  return NULL;
}

int
hopt_indexof (const char *opt_name, Hipopts *options)
{
  int i;
  
  for (i = options->count - 1; i > -1; i--)
    if (!strcmp(options->opts[i]->name, opt_name))
      return options->opts[i]->index;
  
  return -1;
}

int
hopt_isset (const char *opt_name, Hipopts *options)
{
  int i;
  
  if (options == NULL) return 0;
  
  for (i = 0; i < options->count; i++)
    if (!strcmp(options->opts[i]->name, opt_name)) return 1;
  
  return 0;
}

int
hopt_lastopt (Hipopts *options)
{
  if (options == NULL) return -1;
  
  return options->lastopt;
}

void
hopt_free (Hipopts *options)
{
  int i;
  
  for (i = 0; i < options->count; i++) {
    free(options->opts[i]->name);
    if (options->opts[i]->value != NULL) free(options->opts[i]->value);
    free(options->opts[i]);
  }
  free(options);
}

